#include <WiFi.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <Preferences.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <driver/i2s.h>
#include "BaiduSpeech.h"
#include "BaiduTTS.h"

// 全局变量
BLECharacteristic *pCharacteristic;
bool deviceConnected = false; // 蓝牙是否连接
Preferences preferences;

// 定义WIFI用户名密码
String ssid;
String password;

// 定义deepseek paikey
String apiKey = "sk-cf66474f2c5f4cdcad7cd613a5172388";

// 定义百度语音识别API密钥
String baiduApiKey = "0EgCpSDA8W7ns0wJTgFDWgw8";
String baiduSecretKey = "Lxn29kUekGpaI3Ytbm2laTVX7m6xOKX3";
BaiduSpeech baiduSpeech(baiduApiKey, baiduSecretKey);
BaiduTTS baiduTTS(baiduApiKey, baiduSecretKey);

// 定义蓝牙服务和特征的UUID
#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

// 定义 麦克风I2S 配置
#define I2S_WS 15
#define I2S_SD 22
#define I2S_SCK 23
#define I2S_PORT I2S_NUM_0

// 保存WiFi配置
void saveWiFiConfig(const char* ssid, const char* password) {
    preferences.begin("wifi", false);
    preferences.putString("ssid", ssid);
    preferences.putString("password", password);
    preferences.end();
}

// 加载WiFi配置
bool loadWiFiConfig() {
    preferences.begin("wifi", true);
    ssid = preferences.getString("ssid", "");
    password = preferences.getString("password", "");
    preferences.end();
    return ssid.length() > 0 && password.length() > 0;
}

// 连接WIFI
bool connectWifi(){
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid.c_str(), password.c_str());
    
    int attempts = 0;
    while (WiFi.status() != WL_CONNECTED && attempts < 20) { // 20秒超时
        delay(1000);
        Serial.print(".");
        attempts++;
    }
    
    if (WiFi.status() == WL_CONNECTED) {
        Serial.println("");
        Serial.println("WiFi connected");
        Serial.print("IP address: ");
        Serial.println(WiFi.localIP());
        return true;
    } else {
        Serial.println("\nWiFi connection failed");
        return false;
    }
}

// 定义回调类
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
      Serial.println("已连接设备");
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
      Serial.println("已断开设备");
      // 重新启动广播
      BLEDevice::startAdvertising();
      Serial.println("重新启动广播");
    };
};

// 自定义特征回调类
class MyCharacteristicCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic * pCharacteristic) {
        String value = pCharacteristic->getValue();
        if (value.length() > 0) {
          Serial.print("接收到数据：");
          Serial.println(value);
          
          // 清理数据，移除所有的0字节
          String cleanData = "";
          for(int i = 0; i < value.length(); i++) {
            if(value[i] != 0) {
              cleanData += value[i];
            }
          }
          
          Serial.print("清理后的数据：");
          Serial.println(cleanData);
          
          // 使用字符数组方式查找冒号
          char charArray[100];
          cleanData.toCharArray(charArray, sizeof(charArray));
          int colonIndex = -1;
          for(int i = 0; i < cleanData.length(); i++) {
            if(charArray[i] == ':') {
              colonIndex = i;
              break;
            }
          }
          if (colonIndex != -1) {
            String newSsid = cleanData.substring(0, colonIndex);
            String newPassword = cleanData.substring(colonIndex + 1);
            Serial.println(newSsid);
            Serial.println(newPassword);

            // 断开当前WiFi连接
            WiFi.disconnect();
            delay(1000);
            
            // 更新并保存WiFi凭据
            ssid = newSsid;
            password = newPassword;
            saveWiFiConfig(ssid.c_str(), password.c_str());
            
            // 重新连接WiFi
            if (connectWifi()) {
              // 发送连接结果回给客户端
              String response = "WiFi Connected. IP: " + WiFi.localIP().toString();
              pCharacteristic->setValue(response.c_str());
              pCharacteristic->notify();
            }
          }
        }
    }
};

// 调用DeepSeek API
String DeepSeek_Get(String query) {
  if(apiKey.length() == 0){
      return "api_key 不能为空";
  }
  HTTPClient http;

  // 构建请求头
  http.begin("https://api.deepseek.com/chat/completions");
  http.setTimeout(15000);
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Authorization", "Bearer " + apiKey);

  // 构建JSON请求体
  DynamicJsonDocument doc(4096); // 增加大小以容纳角色预设
  doc["model"] = "deepseek-chat";
  
  JsonArray messages = doc.createNestedArray("messages");
  
  // 添加系统角色消息，设定AI的角色预设
  JsonObject systemMessage = messages.createNestedObject();
  systemMessage["role"] = "system";
  systemMessage["content"] = "你是芦大师的私人助理，你的回答应该活泼可爱，使用亲切友好的语气，偶尔可以使用可爱的语气词和表情符号。你应该表现得非常贴心，随时准备帮助芦大师解决问题。";
  
  // 添加用户消息
  JsonObject message = messages.createNestedObject();
  message["role"] = "user";
  message["content"] = query; // 使用传入的查询参数

  doc["stream"] = false;
  doc["max_tokens"] = 512;
  doc["temperature"] = 0.7;
  doc["top_p"] = 0.7;
  doc["top_k"] = 50;
  doc["frequency_penalty"] = 0.5;
  doc["n"] = 1;

  JsonArray stop = doc.createNestedArray("stop");
  stop.add("null");

  JsonObject response_format = doc.createNestedObject("response_format");
  response_format["type"] = "text";

  // 序列化JSON
  String requestBody;
  serializeJson(doc, requestBody);
  Serial.println("Request Body:\n" + requestBody);

  // 发送POST请求
  int httpCode = http.POST(requestBody);
  String response = "";

  // 处理响应
  if (httpCode == HTTP_CODE_OK) {
    String payload = http.getString();
    Serial.println("Raw Response:\n" + payload);

    // 解析JSON响应
    DynamicJsonDocument resDoc(3072);
    DeserializationError error = deserializeJson(resDoc, payload);
    
    if (!error) {
      if (resDoc.containsKey("choices")) {
        response = resDoc["choices"][0]["message"]["content"].as<String>();
      } else {
        Serial.println("No 'choices' in response");
      }
    } else {
      Serial.print("JSON parse failed: ");
      Serial.println(error.c_str());
    }
  } else {
    Serial.printf("HTTP Error: %d - %s\n", httpCode, http.errorToString(httpCode).c_str());
  }

  http.end();
  return response;
}

// 初始化蓝牙设备
void initBluetooth(){
  // 初始化蓝牙设备
  BLEDevice::init("LMB-Robot-01");

  // 创建蓝牙服务器
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // 创建蓝牙服务
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // 创建蓝牙特征
  pCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID,
                      BLECharacteristic::PROPERTY_READ |
                      BLECharacteristic::PROPERTY_WRITE
                    );

  pCharacteristic->setCallbacks(new MyCharacteristicCallbacks());
  // 为特征添加描述符
  pCharacteristic->addDescriptor(new BLE2902());

  // 启动服务
  pService->start();

  // 启动广播
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMaxPreferred(0x12);
  BLEDevice::startAdvertising();
  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

const int bootButtonPin = 9;  // BOOT按钮连接到GPIO0
volatile bool buttonPressed = false;  // 用于标记按钮是否被按下
volatile bool buttonReleased = false;  // 用于标记按钮是否被松开
volatile unsigned long lastDebounceTime = 0;  // 上次按钮状态变化的时间
const unsigned long debounceDelay = 100;  // 消抖延时（毫秒）
volatile bool isReadMicrophone = false; // 是否读取麦克风数据
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;  // 用于中断保护的互斥锁

// 初始化I2S麦克风
void initI2SMicrophone(){
  // 配置 I2S 参数
  i2s_config_t i2s_config = {
    .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate = 16000,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,  // 使用32位采样以适应24位麦克风
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 8,
    .dma_buf_len = 1024,
    .use_apll = false
  };

  // 配置 I2S 引脚
  i2s_pin_config_t pin_config = {
    .bck_io_num = I2S_SCK,
    .ws_io_num = I2S_WS,
    .data_out_num = -1,
    .data_in_num = I2S_SD
  };

  // 安装 I2S 驱动
  i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_PORT, &pin_config);
}

  static uint8_t* audioData = NULL;
  static size_t audioDataSize = 0;
  static size_t audioDataCapacity = 0;
  static bool isRecording = false;
  static unsigned long recordStartTime = 0;
  const unsigned long maxRecordTime = 55000; // 最长录音时间5秒
  
  const int bufferSize = 512; // 减小缓冲区大小，降低每次处理的数据量
  int32_t audioBuffer[bufferSize/4]; // 使用32位缓冲区以适应24位样本
  size_t bytesRead;

// 读取I2S麦克风并进行语音识别
void readI2SMicrophone(){
  // 从 I2S 读取音频数据
  esp_err_t result = i2s_read(I2S_PORT, audioBuffer, bufferSize, &bytesRead, 100 / portTICK_PERIOD_MS);
  
  if (result == ESP_OK) {
      // 打印读取的字节数
      // 简单的数据验证 - 检查是否有音频信号
      int32_t maxValue = 0;
      for (int i = 0; i < bytesRead/4; i++) {
          if (abs(audioBuffer[i]) > maxValue) {
              maxValue = abs(audioBuffer[i]);
          }
      }
    
      // 开始录音
      if (!isRecording) {
          isRecording = true;
          recordStartTime = millis();
          // 确保在开始新录音前释放之前的内存
          if (audioData != NULL) {
              free(audioData);
              audioData = NULL;
          }
          audioDataSize = 0;
          audioDataCapacity = 0;
          Serial.println("开始录音...");
      }
      
      // 将音频数据转换为16位PCM格式并存储
      if (isRecording) {
          // 计算需要的新容量
          size_t newDataSize = audioDataSize + bytesRead/2; // 将32位转换为16位，所以大小减半
          
          // 打印当前内存使用情况
          Serial.printf("当前内存使用: %d 字节, 需要: %d 字节\n", audioDataSize, newDataSize);
          
          // 如果需要更多空间，重新分配内存
          if (newDataSize > audioDataCapacity) {
              // 更保守的内存增长策略，每次只增加512字节
              audioDataCapacity = newDataSize + 512; 
              
              // 打印内存分配信息
              Serial.printf("尝试分配内存: %d 字节\n", audioDataCapacity);
              
              uint8_t* newData = NULL;
              if (audioData == NULL) {
                  // 首次分配内存
                  newData = (uint8_t*)malloc(audioDataCapacity);
                  Serial.println("首次分配内存");
              } else {
                  // 先尝试分配新内存，成功后再复制数据并释放旧内存，避免realloc可能导致的问题
                  newData = (uint8_t*)malloc(audioDataCapacity);
                  if (newData) {
                      // 手动复制数据
                      memcpy(newData, audioData, audioDataSize);
                      // 释放旧内存
                      free(audioData);
                      Serial.println("扩展内存成功");
                  }
              }
              
              if (newData) {
                  audioData = newData;
                  Serial.printf("内存分配成功，当前容量: %d 字节\n", audioDataCapacity);
              } else {
                  Serial.println("内存分配失败!");
                  // 打印可用堆内存
                  Serial.printf("可用堆内存: %d 字节\n", ESP.getFreeHeap());
                  // 内存分配失败时，释放已有内存并重置状态
                  if (audioData != NULL) {
                      free(audioData);
                      audioData = NULL;
                  }
                  audioDataSize = 0;
                  audioDataCapacity = 0;
                  isRecording = false;
                  return;
              }
          }
          
          // 将32位音频数据转换为16位并存储
          for (int i = 0; i < bytesRead/4; i++) {
              // 检查是否还有足够的空间
              if (audioDataSize + 2 > audioDataCapacity) {
                  Serial.println("警告：内存空间不足，提前结束录音");
                  isRecording = false;
                  break;
              }
              
              // 将32位值适当缩放到16位范围，保持更好的信号质量
              int32_t raw_sample = audioBuffer[i];
              int16_t sample = (raw_sample >> 16) & 0xFFFF;
              // 存储16位样本（小端序）
              audioData[audioDataSize++] = sample & 0xFF;
              audioData[audioDataSize++] = (sample >> 8) & 0xFF;
          }
          
          // 每处理一定量的数据后检查可用内存
          if (audioDataSize % 1024 == 0) {
              Serial.printf("已录制 %d 字节，可用堆内存: %d 字节\n", audioDataSize, ESP.getFreeHeap());
              
              // 如果可用内存低于阈值，提前结束录音
              if (ESP.getFreeHeap() < 10000) { // 10KB阈值
                  Serial.println("内存不足，提前结束录音");
                  isRecording = false;
              }
          }
          
          // 检查是否达到最大录音时间或按钮已松开（isReadMicrophone为false）
          if (millis() - recordStartTime >= maxRecordTime || !isReadMicrophone) {
              // 结束录音并进行语音识别
              isRecording = false;
              Serial.println("录音结束，准备语音识别...");
              Serial.print("录制了 ");
              Serial.print(audioDataSize);
              Serial.println(" 字节的音频数据");
              
              // 检查可用内存是否足够进行语音识别
              int freeHeap = ESP.getFreeHeap();
              Serial.printf("语音识别前可用堆内存: %d 字节\n", freeHeap);
              
              // 确保有足够的内存进行语音识别（至少需要音频数据大小的2倍）
              if (freeHeap < audioDataSize * 2 || audioDataSize == 0) {
                  Serial.println("内存不足或没有录制到音频数据，将释放音频数据");
              } else if (WiFi.status() == WL_CONNECTED) {
                  // 在进行语音识别前，先进行垃圾回收
                  ESP.getFreeHeap(); // 触发内部垃圾回收
                  delay(100); // 给系统一些时间进行内存整理
                  
                  Serial.println("开始语音识别...");
                  String recognizedText = baiduSpeech.speechToText(audioData, audioDataSize);
                  Serial.println("识别结果: " + recognizedText);
                  
                  // 检查语音识别后的内存状态
                  Serial.printf("语音识别后可用堆内存: %d 字节\n", ESP.getFreeHeap());
                  
                  // 可以在这里添加对识别文本的进一步处理
                  // 例如调用DeepSeek_Get进行对话
                  if (recognizedText.length() > 0 && !recognizedText.startsWith("识别错误") && !recognizedText.startsWith("HTTP错误")) {
                      // 在调用AI前再次检查内存
                      if (ESP.getFreeHeap() < 10000) {
                          Serial.println("内存不足，跳过AI回复");
                      } else {
                          String aiResponse = DeepSeek_Get(recognizedText);
                          Serial.println("AI回复: " + aiResponse);
                          // 使用TTS播放AI回复
                          // 先清理I2S缓冲区，确保播放流畅
                          baiduTTS.clearBuffer();
                          // 给系统更多时间准备
                          delay(200);
                          // 释放一些内存以确保TTS有足够资源
                          ESP.getFreeHeap();
                          delay(50);
                          
                          // 分段处理较长的文本，避免一次性处理过多数据
                          if (aiResponse.length() > 100) {
                              // 在标点符号处分段
                              int lastPos = 0;
                              int pos = 0;
                              String segment;
                              
                              // 查找标点符号作为分段点
                              while ((pos = aiResponse.indexOf('。', lastPos)) != -1 || 
                                     (pos = aiResponse.indexOf('，', lastPos)) != -1 || 
                                     (pos = aiResponse.indexOf('；', lastPos)) != -1 || 
                                     (pos = aiResponse.indexOf('！', lastPos)) != -1 || 
                                     (pos = aiResponse.indexOf('？', lastPos)) != -1) {
                                  
                                  // 提取分段并播放
                                  segment = aiResponse.substring(lastPos, pos + 1);
                                  if (segment.length() > 5) { // 确保分段有足够长度
                                      Serial.println("播放分段: " + segment);
                                      if (baiduTTS.textToSpeech(segment)) {
                                          Serial.println("分段播放成功");
                                      } else {
                                          Serial.println("分段播放失败");
                                      }
                                      // 每个分段之间短暂暂停
                                      delay(100);
                                      // 清理缓冲区
                                      baiduTTS.clearBuffer();
                                  }
                                  lastPos = pos + 1;
                                  
                                  // 如果已处理到文本末尾，退出循环
                                  if (pos >= aiResponse.length() - 1) break;
                              }
                              
                              // 处理最后一段
                              if (lastPos < aiResponse.length()) {
                                  segment = aiResponse.substring(lastPos);
                                  if (segment.length() > 0) {
                                      Serial.println("播放最后分段: " + segment);
                                      if (baiduTTS.textToSpeech(segment)) {
                                          Serial.println("最后分段播放成功");
                                      } else {
                                          Serial.println("最后分段播放失败");
                                      }
                                  }
                              }
                              
                              Serial.println("TTS播放完成");
                          } else {
                              // 短文本直接播放
                              if (baiduTTS.textToSpeech(aiResponse)) {
                                  Serial.println("TTS播放成功");
                              } else {
                                  Serial.println("TTS播放失败");
                              }
                          }
                      }
                  }
              } else {
                  Serial.println("WiFi未连接，无法进行语音识别");
              }
              
              // 清空音频数据并释放内存，准备下一次录音
              if (audioData != NULL) {
                  free(audioData);
                  audioData = NULL;
              }
              audioDataSize = 0;
              audioDataCapacity = 0;
              return; // 立即返回，不再继续处理音频数据
          }
      }
  } else {
      Serial.print("I2S read error: ");
      Serial.println(result);
      delay(100); // 错误时短暂延迟
  }
  
  // 减少延迟时间以更频繁地处理音频数据
  delay(10);
}

// 统一的中断服务函数，处理按下和释放事件
void IRAM_ATTR buttonISR() {
  unsigned long currentTime = millis();
  if (currentTime - lastDebounceTime > debounceDelay) {
    // 读取当前按钮状态
    int buttonState = digitalRead(bootButtonPin);
    
    portENTER_CRITICAL_ISR(&mux);
    // 高电平表示按钮释放（因为使用了INPUT_PULLUP）
    if (buttonState == HIGH) {
      buttonReleased = true;
      buttonPressed = false;
    } else {
      buttonPressed = true;
      buttonReleased = false;
    }
    portEXIT_CRITICAL_ISR(&mux);
    
    lastDebounceTime = currentTime;
  }
}

void setup() {
    Serial.begin(115200);
    
    // 尝试加载并连接存储的WiFi配置
    if (loadWiFiConfig()) {
        Serial.println("找到存储的WiFi配置");
        if (!connectWifi()) {
            Serial.println("使用存储的配置连接失败，等待蓝牙配置");
        }
    } else {
        Serial.println("未找到存储的WiFi配置，等待蓝牙配置");
    }
    
    initBluetooth();
    initI2SMicrophone();
    i2s_start(I2S_PORT);
    
    // 将BOOT按钮引脚设置为输入模式，并启用上拉电阻
    pinMode(bootButtonPin, INPUT_PULLUP);
    // 为按钮引脚设置中断，使用CHANGE模式同时检测上升沿和下降沿
    attachInterrupt(digitalPinToInterrupt(bootButtonPin), buttonISR, CHANGE);

    // 初始化百度语音识别和TTS
    if (WiFi.status() == WL_CONNECTED) {
        if (baiduSpeech.init()) {
            Serial.println("百度语音识别初始化成功");
        } else {
            Serial.println("百度语音识别初始化失败");
        }
        
        if (baiduTTS.init()) {
            Serial.println("百度TTS初始化成功");
            // 测试TTS功能前先清理缓冲区
            baiduTTS.clearBuffer();
            // 给系统一些时间准备
            delay(200);
            // 释放一些内存
            ESP.getFreeHeap();
            delay(50);
            
            // 测试TTS功能
            if (baiduTTS.textToSpeech("网络连接成功")) {
                Serial.println("TTS测试成功");
                // 播放完成后再次清理缓冲区
                delay(100);
                baiduTTS.clearBuffer();
            } else {
                Serial.println("TTS测试失败");
            }
        } else {
            Serial.println("百度TTS初始化失败");
        }
    }

    String message = "你好呀";
    String result = DeepSeek_Get(message);
    Serial.println(result);
    // 先清理I2S缓冲区，确保播放流畅
    baiduTTS.clearBuffer();
    // 给系统更多时间准备
    delay(200);
    // 释放一些内存以确保TTS有足够资源
    ESP.getFreeHeap();
    delay(50);
    
    // 直接播放整段文本
    if (baiduTTS.textToSpeech(result)) {
        Serial.println("TTS播放成功");
    } else {
        Serial.println("TTS播放失败");
    }
    
}

void loop() {
    // 主循环
    bool currentPressed = false;
    bool currentReleased = false;
    
    portENTER_CRITICAL(&mux);
    currentPressed = buttonPressed;
    currentReleased = buttonReleased;
    if (currentPressed) buttonPressed = false;
    if (currentReleased) buttonReleased = false;
    portEXIT_CRITICAL(&mux);
    
    if (currentPressed) {
      Serial.println("BOOT按钮被按下!");
      // 按钮被按下，开始读取麦克风数据
      isReadMicrophone = true;
    }
    
    if (currentReleased) {
      Serial.println("BOOT按钮被释放!");
      // 按钮被松开，结束读取麦克风数据
      isReadMicrophone = false;
      readI2SMicrophone();
    }
    
    if(isReadMicrophone) {
      // 读取麦克风数据
      readI2SMicrophone();
    }
    delay(10);
}

